package bl.tools;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import ml.options.OptionSet;
import ml.options.Options;

public class Lists2CSV {
	
	private String csv, folder;
	
	enum Level {PACKAGE, CLASS, METHOD};
	
	Level level;
	
	private ArrayList<String> input;
	
	private static final String sep = ";";
	
	private static final String ps = "\\";
	
	private LOCCParser lp;
	
	private class CSVline implements Comparator<CSVline>{
		private double infoGain;
		private String signature;
		private int loc;
		CSVline(double infoGain, String signature){
			this.infoGain = infoGain;
			this.signature = signature;
		}
		public CSVline() {
		}
		String toCSVString(){
			return this.infoGain + sep + this.signature + sep + this.loc + "\n";
		}
		public String getSignatue() {
			return this.signature;
		}
		@Override
		public int compare(CSVline o1, CSVline o2) {
			if(o1.infoGain < o2.infoGain) return -1;
			if(o1.infoGain > o2.infoGain)	return 1;
			else{
				if(level == Level.METHOD){
					o1.loc = lp.getMethodLoc(o1.getSignatue());
					o2.loc = lp.getMethodLoc(o2.getSignatue());
				} else if(level == Level.CLASS){
					o1.loc = lp.getClassLoc(o1.getSignatue());
					o2.loc = lp.getClassLoc(o2.getSignatue());
				} else {
					o1.loc = lp.getPackageLoc(o1.getSignatue());
					o2.loc = lp.getPackageLoc(o2.getSignatue());
				}
				if(o1.loc < o2.loc) return -1;
				if(o1.loc > o2.loc)	return 1;
				return 0;
			}
		}
	}
	
	public Lists2CSV(String out, String folder, ArrayList<String> data, String in) throws FileNotFoundException {
		this.csv = out;
		this.input = data;
		this.folder = folder;
		this.lp = new LOCCParser(folder + ps + in);
	}

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		Options opt = new Options(args);
		opt.addSet("foo", 1, 60).addOption("o", Options.Separator.BLANK,
							   Options.Multiplicity.ONCE).addOption(
							   "folder", Options.Separator.BLANK,
							   Options.Multiplicity.ONCE).addOption(
							   "i", Options.Separator.BLANK,
							   Options.Multiplicity.ONCE);
		OptionSet set = opt.getMatchingSet(false, true);
		
		if (set == null) {
			System.out.println("Usage:\n---------------------\n" +
					"\t-o \t output CSV file\n" +
					"\t-folder \t path pre-fix for the files\n"+
					"\t-i \t file with size infos from LOCC");
			System.out.println("\nThe options library says:\n" + opt.getCheckErrors());
			System.exit(1);
		}
		
		//evaluate options
		String folder = set.getOption("folder").getResultValue(0);
		ArrayList<String> data = set.getData();
		String out = set.getOption("o").getResultValue(0);
		System.out.println("Output CSV: " + folder + ps + out +
				"\n\nGathering data in files:\n");
		for(String file : data){
			System.out.println(folder + ps + file);
		}
		String in = set.getOption("i").getResultValue(0);
		
		Lists2CSV lists2csv = new Lists2CSV(out, folder, data, in);
		lists2csv.exec();
		
	}
	
	void exec() throws IOException{
		writeCSV( getSignaturesSorted(readSignatures(folder, input) ) );
	}
	
	private void writeCSV(ArrayList<CSVline> signatures) throws IOException {
		String fileName = folder + ps + csv;
		File f = new File(fileName);
		if(f.exists()){
			System.out.println(fileName + " already exists!");
			System.exit(1);
		}
		FileWriter fw = new FileWriter(f);
		BufferedWriter br = new BufferedWriter(fw);
		Collections.reverse(signatures);
		for(CSVline line : signatures){
			br.write(line.toCSVString());
		}
		br.close();
	}

	/**
	 * @throws IOException 
	 * 
	 */
	ArrayList<CSVline> readSignatures(String folder, ArrayList<String> input) throws IOException{
		ArrayList<CSVline> out = new ArrayList<CSVline>();
		String line = null;
		for(String file : input){
			FileReader fr = new FileReader(folder + ps + file);
			BufferedReader br = new BufferedReader(fr);
			while(true){
				line = br.readLine();
				if(line == null)
					break;
				// get to the right line
				if(line.equals("Entropy Score NOT normalized:")){
					br.readLine();
					while(true){
						line = br.readLine();
						if(line.equals(""))
							break;
						if(line.indexOf("foreign") == -1)
							out.add( parseLine(line) );
					}
				}
			}
		}
		return out;
	}
	
	private Level inferLevel(String signature){
		if(signature.indexOf('(') != -1)
			return Level.METHOD;
		if(  Character.isUpperCase( (signature.charAt( signature.lastIndexOf('.') +1 ) ) )  )
			return Level.CLASS;
		else
			return Level.PACKAGE;
	}

	/**
	 * 
	 * @param line a line in a scoring file
	 * @return line parsed into a {@link CSVline}
	 */
	private CSVline parseLine(String line) {
		CSVline out = null;
		String[] splitted = line.split("\\s+");
		if(splitted.length != 2)
			throw new RuntimeException("Splitting: " + line + "\n\tresultet in " + line.length() + " parts! Only two are excepted.");
		out = new CSVline( Double.parseDouble(splitted[0].replace(',', '.')), splitted[1] );
		if(level == null)
			level = inferLevel(splitted[1]);
		else {
			if(!(level == inferLevel(splitted[1]) ) )
				throw new RuntimeException("Infering level did not work!");
		}
		return out;
	}
	
	ArrayList<CSVline> getSignaturesSorted(ArrayList<CSVline> in){
		Collections.sort(in, new CSVline());
		return in;
	}
	
//	int getLoc(String classSignature){
//		String line, java = "";
//		int loc = 0, blocks = 1, offset = 0, end, java_l;
//		boolean withinComment = false, withinInnerClass = false, outerClass = false;
//		FileReader fr = null;
//		
//		String sig = classSignature.replace('.', '\\');
//		String file1 = codePath + ps + "src" + ps + sig + ".java";
//		String file2 = codePath + ps + "toolsrc" + ps + sig + ".java";
//		try {
//			fr = new FileReader(file1);
//		} catch (FileNotFoundException e) {
//			try {
//				fr = new FileReader(file2);
//			} catch (FileNotFoundException e1) {
//				try {
//					return getLocInnerClass(classSignature);
//				} catch (IOException e2) {
//					e2.printStackTrace();
//				}
//			}
//		}
//		BufferedReader br = new BufferedReader(fr);
//		try {
//			while( (line = br.readLine()) != null ){
//				java += line + "\n";
//			}
//		} catch (IOException e1) {
//			e1.printStackTrace();
//		}
//		java_l = java.length();
//		while( offset < java_l ){
//			while( offset < java_l && java.charAt(offset) == '\n' ) offset++; // no blank lines
//			end = java.indexOf('\n', offset); // EOL
//			end = end != -1 ? end : java.length(); // EOF
//			line = java.substring(offset, end); 
//			int comment = line.indexOf("//");
//			if(comment != -1){
//				offset += line.length() - comment;
//				line = line.substring(0, comment);
//			}
//			// take care that we are not within a multi line comment
//			int mlc_si = line.indexOf("/*");
//			int mlc_ei = line.indexOf("*/");
//			if(mlc_si != -1){
//				offset += line.length() - mlc_si;
//				line = line.substring(0, mlc_si);
//				withinComment = true;
//			}
//			if(mlc_ei != -1){
//				int l = line.length();
//				offset += l - mlc_ei;
//				withinComment = false;
//				if(l-2-mlc_ei < 1) continue;
//				line = line.substring( mlc_ei +2, l );
//			}
//			// do split here
//			boolean classKeyWord = line.matches(".*class\\s+\\w");
//			if(classKeyWord && outerClass && !withinComment){
//				withinInnerClass = true;
//				offset = java.indexOf('{', offset) +1;
//				continue;
//			}
//			if(!outerClass && classKeyWord && !withinComment) outerClass = true;
//			if( withinInnerClass && line.matches(".*\\{.*") ) blocks++;
//			if( withinInnerClass && line.matches(".*\\}.*") ) blocks--;
//			// take care that we are not within a multi line comment
//			if( blocks ==  0 )
//				withinInnerClass = false;
//			// from here on increment only if we read a command or the begin of a block
//			if(  !withinComment && (!withinInnerClass || countInnerClasses) &&( line.matches(".*;.*") || line.matches(".*\\w*\\(.*") )  )
//				loc++;
//			offset += line.length();
//		}
//		return loc;
//	}
	
//	int getLocInnerClass(String signature) throws IOException{
//		String qualifier = signature.substring(0, signature.lastIndexOf('.')).replace('.', '\\');
//		String folder = codePath + ps + "toolsrc" + ps + qualifier;
////		System.out.println(folder);
//		File f = new File(folder);
//		File[] tmp = f.listFiles();
//		ArrayList<File> files = null;
//		if(tmp != null)
//			files = new ArrayList<File>(Arrays.asList(tmp));
//		f = new File(codePath + ps + "src" + ps + qualifier);
//		tmp = f.listFiles();
//		if(files != null)
//			files.addAll(Arrays.asList(tmp));
//		else
//			files = new ArrayList<File>(Arrays.asList(tmp));
//		BufferedReader br = null;
//		int loc = 0;
//		for(File file : files){
//			br = new BufferedReader(new FileReader(file));
//			loc = LocCount.countRegExpLoc(br, makeSignatureRegExp(signature));
//			if(loc != 0)
//				return loc;
//		}
//		return 0;
//	}
//	
//	private static String makeSignatureRegExp(String signature) {
//		int dot_i, offset = 0;
//		while( (dot_i = signature.indexOf('.', offset +1) ) != -1){
//			offset = dot_i;
//		}
//		signature = signature.substring(offset +1);
//		
//		return ".*class\\s+" + signature + ".*";
//	}
//	
}
